package jude.java.money.net.http;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.httpclient.Cookie;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HostConfiguration;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpMethodBase;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.NTCredentials;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.cookie.CookiePolicy;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class HttpAuthenticator {
	private static final Log LOG = LogFactory.getLog(HttpAuthenticator.class);
	
	private HttpClient httpClient;
	private String[] proxyConfigs = null;
	private List<HttpCommunication> communications;
	private int auauthenticationCount = 1;
	private final static int HTTP_TIMEOUT = 60000;
	
	private boolean first = true;
	
	public void addCommunication(HttpCommunication communication){
		if(communications == null){
			communications = new ArrayList<HttpCommunication>();
		}
		communications.add(communication);
	}
	
	public String[] getProxyConfigs() {
		return proxyConfigs;
	}

	public void setProxyConfigs(String[] proxyConfigs) {
		this.proxyConfigs = proxyConfigs;
	}

	public HttpClient getHttpClient() {
		return httpClient;
	}

	public void setHttpClient(HttpClient httpClient) {
		this.httpClient = httpClient;
	}

	public String authenticatorAndDownload(String targetFilePath) {
		LOG.info("http authenticatorAndDownload");
		if(first){
			initHttpClientProxy();
		}
		httpClient.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY);
		
		for(int i=0, j=communications.size(); i < j; i++){
			HttpCommunication c = communications.get(i);
			LOG.info("��: " + c.getUrl());
			HttpMethodBase method = c.getHttpMethod();
			if(StringUtils.isNotEmpty(c.getPrefer()))
				method.setRequestHeader("Preferer", c.getPrefer());
			
			int statuscode = 0;
			String response = null;
			try {
				logCookies("cookies before login");
				executeMethod(method);
//				httpClient.executeMethod(loginMethod);
				statuscode = method.getStatusCode();
				logCookies("cookies after login");
				response = getResponseContent(method, c.getCharset());
				
				if (isRedirect(statuscode)) {
					if(c.isFollowRedirects()){
						Header header = method.getResponseHeader("Location");
						if (header != null) {
							String newUrl = header.getValue();
							if ((newUrl == null)){// || (newUrl.equals(""))
								newUrl = StringUtils.substringBeforeLast(c.getUrl().replace('\\', '/'), "/");
							}
							LOG.info("redirect to: " + newUrl);
							method.releaseConnection();
							method = HttpCommunication.getRedirectMethod(newUrl);
							executeMethod(method);
							statuscode = method.getStatusCode();
							response = getResponseContent(method, c.getCharset());
							LOG.info("redirect success");
							logCookies("cookies after redirect");
						} else {
							fail("Invalid redirect");
						}
					} else {
						LOG.info("need redirect, but setting not allow redirect");
						writeFile(response, "E:\\shjh" + i + ".html", c.getCharset());
						continue;
					}
				}
				if (statuscode == HttpStatus.SC_OK){
					if (c.isFailPage(response)) {
						fail("login fail");
					}else if (c.isSuccessPage(response)) {
						LOG.info("login success");
						logCookies("cookies after download");
						if(i == j-1){
							writeFile(response, targetFilePath, c.getCharset());
							return c.getCharset();
						}else{
//							writeFile(response, "E:\\shjh" + i + ".html", c.getCharset());
						}
			        } else {
			        	LOG.info("successPattern=" + c.getSuccessPattern());
			        	LOG.info("failPattern=" + c.getFailPattern());
			        	fail("login fail. unknown page. doesn't matcher failPattern and successPattern");
			        }
				} else {
					fail("download fail. statusCode=" + statuscode);
				}
			} catch (Throwable e) {
				e.printStackTrace();
				LOG.error(e);
				LOG.error("exception accur on donwloading page:" + c.getUrl());
				throw new RuntimeException(e);
			} finally {
				method.releaseConnection();
			}
		}
		return null;
	}
	
	public void clearPrevious(){
		if(communications != null && communications.size() > auauthenticationCount){
			for (int i=auauthenticationCount, j=communications.size(); i<j; i++) {
				LOG.info("clear previous authentication:" + communications.get(i).getUrl());
				communications.remove(i);
			}
		}
	}
	
	private void logCookies(String s){
		if(httpClient == null)
			return;
		Cookie[] cookies = httpClient.getState().getCookies();
		LOG.info("[" + cookies.length + "]" + s);
		for (Cookie cookie : cookies) {
			LOG.info(cookie.getDomain() + ":" + cookie.getPath() + ":" + cookie.getName() + "=" + cookie.getValue());
		}
	}
	
	private void executeMethod(HttpMethodBase method) throws HttpException, IOException{
//		HttpState state = httpClient.getState();
//		Cookie[] cookies = state.getCookies();
		httpClient.executeMethod(method);
//		LOG.info("request Cookies == " + method.getRequestHeader("Cookie"));
//		LOG.info("response Cookies == " + method.getResponseHeader("Set-cookie"));
//		state.addCookies(cookies);
	}
	
	private void writeFile(String response, String targetFilePath, String charset) throws IOException{
		new File(targetFilePath).delete();
		OutputStream out = new FileOutputStream(targetFilePath);
		try{
			out.write(response.getBytes(charset));
			LOG.info("����Ϊ��" + targetFilePath);
		}finally{
			out.close();
		}
	}
	
	private void fail(String message){
		LOG.info(message);
		throw new RuntimeException(message);
	}
	
	private void initHttpClientProxy(){
		first = false;
		if(HTTP_TIMEOUT > 0){
			httpClient.getHttpConnectionManager().getParams()
					.setConnectionTimeout(HTTP_TIMEOUT);
			httpClient.getHttpConnectionManager().getParams().setSoTimeout(
					HTTP_TIMEOUT);
		}
		if(proxyConfigs!=null && proxyConfigs.length>=4){
			initHttpClientProxy(proxyConfigs[0], Integer.parseInt(proxyConfigs[1])
					, proxyConfigs[2], proxyConfigs[3]);
		}
	}
	private void initHttpClientProxy(String host, int port, String usr, String pwd) {
		if (StringUtils.isEmpty(host) || port <= 0)
			return;

		if (StringUtils.isNotEmpty(usr) && StringUtils.isNotEmpty(pwd)) {
			UsernamePasswordCredentials creds = null;
			if (usr.indexOf('\\') > -1)
				creds = new NTCredentials(
						StringUtils.substringAfter(usr, "\\"), pwd, host,
						StringUtils.substringBefore(usr, "\\"));
			else
				creds = new UsernamePasswordCredentials(usr, pwd);
			httpClient.getState().setProxyCredentials(AuthScope.ANY, creds);
		}

		HostConfiguration hcf = new HostConfiguration();
		hcf.setProxy(host, port);
		httpClient.setHostConfiguration(hcf);
	}
	
	public void destroy(){
		if(communications != null)
			communications.clear();
		httpClient = null;
	}

	private boolean isRedirect(int statusCode) {
		if ((statusCode == HttpStatus.SC_MOVED_TEMPORARILY) ||
            (statusCode == HttpStatus.SC_MOVED_PERMANENTLY) ||
            (statusCode == HttpStatus.SC_SEE_OTHER) ||
            (statusCode == HttpStatus.SC_TEMPORARY_REDIRECT)) {
        	return true;
        } else {
        	return false;
        }
	}
	
	
	
	public static String getResponseContent(HttpMethodBase method,
			String charset) throws IOException {

		InputStream input = method.getResponseBodyAsStream();

		if (StringUtils.isBlank(charset)) {
			charset = method.getResponseCharSet();
			if (charset == null || !Charset.isSupported(charset)) {
				charset = "ISO-8859-1";
			}
		}

		Reader reader = new BufferedReader(new InputStreamReader(input, charset));
		StringBuffer result = new StringBuffer();

		char[] buffer = new char[2048];
		int length = 0;
		while ((length = reader.read(buffer)) > 0) {
			result.append(buffer, 0, length);
		}
		reader.close();
		input.close();

		return result.toString();
	}

	public void setAuauthenticationCount(int auauthenticationCount) {
		this.auauthenticationCount = auauthenticationCount;
	}
	

}
